package com.web.web.request;

import com.web.farme.application.DefaultContext;
import com.web.farme.redis.RedisCache;
import com.web.farme.tool.StringUtils;
import com.web.web.config.WebConfig;
import com.web.web.session.RedisSession;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.core.env.ConfigurableEnvironment;

import javax.servlet.ServletContext;
import javax.servlet.http.*;
import java.util.UUID;

@Slf4j
public class RedisSessionHttpServletRequest extends HttpServletRequestWrapper {

    private final RedisCache redisCache;

    private final HttpServletResponse response;

    private final ServletContext servletContext;

    private HttpSession redisSession;

    private String sessionId;

    private ConfigurableEnvironment environment;

    private WebConfig webConfig;

    public RedisSessionHttpServletRequest(HttpServletRequest request, HttpServletResponse response, RedisCache redisCache, ServletContext servletContext) {
        super(request);
        this.response = response;
        this.redisCache = redisCache;
        this.servletContext = servletContext;
        this.webConfig = DefaultContext.getConfig(WebConfig.class);
        this.environment = DefaultContext.getEnvironment();
    }

    @Override
    public HttpSession getSession() {
        return getRedisSession(false);
    }

    @Override
    public HttpSession getSession(boolean create) {
        return getRedisSession(create);
    }

    private HttpSession getRedisSession(boolean create) {
        if (this.redisSession == null) {
            if (this.getRequestedSessionId() != null) {
                String createTime = this.redisCache.getMap(this.webConfig.getSessionCacheKeyPre() + this.sessionId, "createTime");
                if (createTime != null) {
                    this.redisSession = new RedisSession(this.redisCache, this.webConfig.getSessionCacheKeyPre() + this.sessionId,
                            Long.parseLong(createTime), this.sessionId, this.servletContext, this.webConfig.getSessionExpireSeconds(), false);
                }
            }
            //如果上面没有创建成功 并且 自动创建时
            if (redisSession == null && create) {
                try {
                    this.sessionId = DigestUtils.md5Hex(UUID.randomUUID().toString());
                    long createTime = System.currentTimeMillis();
                    this.redisCache.setMap(this.webConfig.getSessionCacheKeyPre() + this.sessionId, "createTime", String.valueOf(createTime));
                    this.redisCache.expire(this.webConfig.getSessionCacheKeyPre() + this.sessionId, this.webConfig.getSessionExpireSeconds());
                    this.redisSession = new RedisSession(this.redisCache, this.webConfig.getSessionCacheKeyPre() + sessionId, createTime, this.sessionId,
                            this.servletContext, this.webConfig.getSessionExpireSeconds(), true);
                    if (this.getHeader("origin") != null && this.getHeader("origin").contains("localhost")) {
                        addCookie(this, response, this.webConfig.getSessionCookieName(), sessionId, "localhost", "/", -1);
                    } else {
                        addCookie(this, response, this.webConfig.getSessionCookieName(), sessionId,
                                this.webConfig.getSessionCookieDomain(),
                                StringUtils.isNotBlank(this.webConfig.getPathPrefix())? this.webConfig.getConfigKey() : this.webConfig.getPrefix(),
                                -1);
                    }
                } catch (Exception e) {
                    this.redisSession = null;
                }
            }
        }
        return redisSession;
    }

    public void cleanSession() {
        this.redisCache.delete(this.webConfig.getSessionCacheKeyPre() + this.sessionId);
        this.redisSession = null;
        addCookie(this, response, this.webConfig.getSessionCookieName(), null, this.webConfig.getSessionCookieDomain(), "/", 0);
    }


    /**
     * 根据请求中的cookie 和配置中的cookie名获取 sessionId
     */
    @Override
    public String getRequestedSessionId() {
        if (this.sessionId != null) {
            return this.sessionId;
        }
        String result = this.getParameter(this.webConfig.getSessionCookieName());
        this.sessionId = null == result ? (String) this.getAttribute(this.webConfig.getSessionCookieName()) : result;
        if (this.sessionId == null) {
            this.sessionId = this.getHeader(this.webConfig.getSessionCookieName());
        }
        if (this.sessionId == null) {
            Cookie cookie = findCookie(this, this.webConfig.getSessionCookieName());
            if (cookie != null && cookie.getValue() != null) {
                this.sessionId = cookie.getValue();
            }
        }
        return this.sessionId;
    }

    @Override
    public String changeSessionId() {
        return null;
    }

    @Override
    public boolean isRequestedSessionIdValid() {
        return false;
    }

    @Override
    public boolean isRequestedSessionIdFromCookie() {
        return false;
    }

    @Override
    public boolean isRequestedSessionIdFromURL() {
        return false;
    }

    @Override
    public boolean isRequestedSessionIdFromUrl() {
        return false;
    }


    /**
     * 查找指定请求中的指定名称的Cookie
     */
    public static Cookie findCookie(HttpServletRequest request, String name) {
        if (request != null) {
            Cookie[] cookies = request.getCookies();
            if (cookies != null && cookies.length > 0) {
                for (Cookie cookie : cookies) {
                    if (cookie.getName().equals(name)) {
                        return cookie;
                    }
                }
            }
        }

        return null;
    }


    /**
     * 设置cookie
     *
     * @param request     当前请求
     * @param response    当前相应
     * @param name        名称
     * @param value       值
     * @param domain      域名
     * @param contextPath 路径
     * @param maxAge      有效时间
     */
    public static void addCookie(
            HttpServletRequest request,
            HttpServletResponse response,
            String name,
            String value,
            String domain,
            String contextPath,
            int maxAge) {
        if (request != null && response != null) {
            Cookie cookie = new Cookie(name, value);
            cookie.setMaxAge(maxAge);
            cookie.setSecure(request.isSecure());

            if (contextPath == null || contextPath.isEmpty()) {
                cookie.setPath("/");
            } else {
                cookie.setPath(contextPath);
            }

            if (domain != null && !domain.isEmpty()) {
                cookie.setDomain(domain);
            }

            response.addCookie(cookie);

            log.debug(
                    "Cookie update the sessionID.[name={0},value={1},maxAge={2},secure={3},path={4},domain={5}]",
                    cookie.getName(),
                    cookie.getValue(),
                    String.valueOf(cookie.getMaxAge()),
                    String.valueOf(cookie.getSecure()),
                    cookie.getPath(),
                    cookie.getDomain());
        }
    }
}
